/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
/*
 * TrainingBook
 * Copyright (C) Arnaud Duval 2010 <arnaud.duval@gmail.com>
 * 
 * Projects is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Projects is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "TrainingBook.hpp"

#include <libxml++/libxml++.h>
#include <iostream>
#include <fstream>
#include <filesystem-utils.hpp>
#include <Track.hpp>

#define verboseImport false

///@brief Default constructor
TrainingBook::TrainingBook()
{
    appli = NULL;
}

///@brief Copy constructor
///@param t TrainingBook object to copy
TrainingBook::TrainingBook(const TrainingBook& t)
{
    activityTypes = t.activityTypes;
    activities = t.activities;
    days = t.days;
    osmTilesRepository = t.osmTilesRepository;
}

///@brief Destructor
TrainingBook::~TrainingBook()
{
    appli = NULL;
}

///@brief Overload of = operator
TrainingBook& TrainingBook::operator= (const TrainingBook& t)
{
    activityTypes = t.activityTypes;
    activities = t.activities;
    days = t.days;
    osmTilesRepository = t.osmTilesRepository;
    return (*this);
}

///@brief return ActivityTypes
ActivityTypes TrainingBook::GetActivityTypes()
{
    return activityTypes;
}

///@brief read from a xml file
///@param path Path to the xml file to read
bool TrainingBook::ReadXML(std::string path)
{
    //Check if the given file exists
    if(!FileExists(path))
        return false;
  
    try
    {
        xmlpp::DomParser parser;
        parser.set_substitute_entities();
        parser.parse_file(path);
        if(parser)
        {
            //Walk the tree
            const xmlpp::Node* pNode = parser.get_document()->get_root_node();
            const xmlpp::ContentNode* nodeContent = dynamic_cast<const xmlpp::ContentNode*>(pNode);
            const xmlpp::TextNode* nodeText = dynamic_cast<const xmlpp::TextNode*>(pNode);
            const xmlpp::CommentNode* nodeComment = dynamic_cast<const xmlpp::CommentNode*>(pNode);

            Glib::ustring nodename = pNode->get_name();
            if(nodename == "TrainingBook")  //root node corresponds to a TrainingBook
            {
                xmlpp::Node::NodeList list = pNode->get_children();
                for(xmlpp::Node::NodeList::iterator iter = list.begin();
                    iter != list.end();
                    ++iter)
                {
                    xmlpp::Node* currentNode = dynamic_cast<const xmlpp::Node*>(*iter);
                    if(currentNode->get_name() == "ActivityTypes")
                    ///@brief Entering list of activity types
                    {
                        activityTypes.ReadXMLNode(currentNode);
                    }
                    else if(currentNode->get_name() == "Activities")
                    ///@brief Entering list of activities
                    {
                        xmlpp::Node::NodeList listOfActivities
                            = currentNode->get_children();
                        for(xmlpp::Node::NodeList::iterator iterAct = listOfActivities.begin();
                            iterAct != listOfActivities.end();
                            ++iterAct)
                        {
                            xmlpp::Node* currentActNode = dynamic_cast<const xmlpp::Node*>(*iterAct);
                            if(currentActNode->get_name() == "ActivityHeader")
                            {
                                ActivityHeader newActivityHeader;
                                newActivityHeader.ReadXMLNode(currentActNode);
                                AddActivityHeader(newActivityHeader);
                            }
                        }
                    }
                    else if(currentNode->get_name() == "OsmTilesRepository")
                    {
                        if(currentNode->get_children().size() != 0)
                            osmTilesRepository = ((xmlpp::TextNode*)(*((currentNode->get_children()).begin())))->get_content();
                    }
                }
            }
        }
    }
    catch(const std::exception& ex)
    {
        std::cerr << "Exception caught: " << ex.what() << std::endl;
    }    
    return true;
}

///@brief write to a xml file
///@param path Path to XML file to write
bool TrainingBook::WriteXML(std::string path)
{
    std::ofstream fout(path.c_str());
    if(!fout)
        return false;
    fout << "<TrainingBook>\n";
    activityTypes.WriteXML(fout);
    fout << "  <Activities>\n";
    std::list<ActivityHeader>::iterator lit(activities.begin()), lend(activities.end());
    for(; lit != lend ; ++lit)
    {
        fout << "    <ActivityHeader>\n";
        (*lit).WriteXML(fout)	;
        fout << "    </ActivityHeader>\n";
    }
    fout << "  </Activities>\n";
    fout << "  <OsmTilesRepository>" << osmTilesRepository << "</OsmTilesRepository>\n";
    fout << "</TrainingBook>\n";
    std::cout << "File written to " << path << "\n";
    return true;
}

///@brief Add a new activity
///@param a Activity to add
void TrainingBook::AddActivity(Activity a)
{
    if(ActivityExistsByDate(a.GetDateTime()))
    {
        std::cerr << "Can't add the activity. An activity with exactly the same date and start time already exists\n";
        std::cerr << "The date is : " << a.GetDateTime() << "\n";
        return;
    }
    if(!DirExists(appli->GetParameter("TRAININGBOOK_DIR")))
        CreateDir(appli->GetParameter("TRAININGBOOK_DIR"));
    if(a.GetHistory().WriteHistoryFile(a.GetHeader().GetId(), appli->GetParameter("TRAININGBOOK_DIR")))
    {
        activities.push_back(a.GetHeader());
        activities.sort();
    }
    else
        std::cerr << "Activity can't be added : there is a problem for writing history\n";
}

///@brief Add a new activity header
///@param a ActivityHeader to add
void TrainingBook::AddActivityHeader(ActivityHeader a)
{
    if(ActivityExistsByDate(a.GetDateTime()))
    {
        std::cerr << "Can't add the activity. An activity with exactly the same date and start time already exists\n";
        std::cerr << "The date is : " << a.GetDateTime() << "\n";
        return;
    }
	
    activities.push_back(a);
    activities.sort();
}

///@brief Add a new user day
///@param d UserDay to add
void TrainingBook::AddUserDay(UserDay d)
{
    days.push_back(d);
}

///@brief Return activity list
std::list<ActivityHeader> &TrainingBook::GetActivities()
{
    return activities;
}

///@brief Return activity with given date
///@param d DateTime object to identify the activity
ActivityHeader TrainingBook::GetActivityByDate(DateTime d) const
{
    ActivityHeader res;
    std::list<ActivityHeader>::const_iterator lit(activities.begin()), lend(activities.end());
    while((lit != lend) && ((*lit).GetDateTime() != d))
    {
        ++lit;
    }
    res = (*lit);
    return res;
}

///@brief Return activity with given id
///@param id Id of activity to return
ActivityHeader TrainingBook::GetActivityById(std::string id) const
{
	ActivityHeader res;
    std::list<ActivityHeader>::const_iterator lit(activities.begin()), lend(activities.end());
    while((lit != lend) && ((*lit).GetId() != id))
    {
        ++lit;
    }
    res = (*lit);
    return res;
}

///@brief Delete an activity with given date
///@param d DateTime object to identify the activity
void TrainingBook::DeleteActivityByDate(DateTime d)
{
    std::list<ActivityHeader>::iterator lit(activities.begin()), lend(activities.end());
    while((lit != lend) && ((*lit).GetDateTime() != d))
    {
        ++lit;
    }
    std::cout << "The history file should be erased !!!!\n";
    activities.erase(lit);
}

///@brief Return true if an activity with given date already exists
///@param d DateTime object representing the date to search
bool TrainingBook::ActivityExistsByDate(DateTime d)
{
    for (std::list<ActivityHeader>::iterator lit = activities.begin() ; lit != activities.end() ; ++lit)
    {
        if((*lit).GetDateTime() == d)
             return true;
    }
    return false;
}


///@brief Update an existing activity
///@param a ActivityHeader object containing the new values
void TrainingBook::UpdateActivity(ActivityHeader a)
{
    std::list<ActivityHeader>::iterator lit(activities.begin()), lend(activities.end());
    while((lit != lend) && ((*lit).GetId() != a.GetId()))
    {
        ++lit;
    }
    (*lit) = a;
}

///@brief Set the activity types
///@param DactivityTypes Activity types to be set
void TrainingBook::SetActivityTypes(ActivityTypes DactivityTypes)
{
    activityTypes = DactivityTypes;
}

///@brief Return the date of the last activity
///@return The date of the hast activity.
DateTime TrainingBook::GetLastActivityDate() const
{
    DateTime res;
    if(activities.empty())
        return res;
    for(std::list<ActivityHeader>::const_iterator lit = activities.begin() ; lit != activities.end() ; ++lit)
    {
        if(lit->GetDateTime() > res)
            res = lit->GetDateTime();
    }
}

///@brief Return the date of the last user day
DateTime TrainingBook::GetLastUserDayDate() const
{
    DateTime res;
    if(days.empty())
        return res;
    for(std::list<UserDay>::const_iterator lit = days.begin() ; lit != days.end() ; ++lit)
    {
        if(lit->GetDate() > res)
            res = lit->GetDate();
    }
}

///@brief Return the last user day
UserDay TrainingBook::GetLastUserDay() const
{
    UserDay res;
    if(days.empty())
        return res;
    DateTime last = GetLastUserDayDate();
    for(std::list<UserDay>::const_iterator lit = days.begin() ; lit != days.end() ; ++lit)
    {
        if(lit->GetDate() == last)
            return (*lit);
    }
}

///@brief Return favourite OSM tiles repository
std::string TrainingBook::GetOsmTilesRepository() const
{
    return osmTilesRepository;
}

///@brief Set favourite OSM tiles repository
///@param DosmTilesRepository URL of OpenStreetMap tiles repository
void TrainingBook::SetOsmTilesRepository(std::string DosmTilesRepository)
{
    osmTilesRepository = DosmTilesRepository;
}

///@brief Set the pointer to the application object
///@param pAppli Pointer to an Application object
void TrainingBook::SetAppli(Application* pAppli)
{
	appli = pAppli;
}

///@brief Read TrainingBook from a SportTrack v3 file *.logbook3
///@param filePath path to the *.logbook3 file to read
bool TrainingBook::ReadSportTrack3File(std::string filePath)
{
    try
    {
        xmlpp::DomParser parser;
        parser.set_substitute_entities();
        parser.parse_file(filePath);
        if(parser)
        {
            //Walk the tree
            const xmlpp::Node* rootNode = parser.get_document()->get_root_node();
            const xmlpp::ContentNode* nodeContent = dynamic_cast<const xmlpp::ContentNode*>(rootNode);
            const xmlpp::TextNode* nodeText = dynamic_cast<const xmlpp::TextNode*>(rootNode);
            const xmlpp::CommentNode* nodeComment = dynamic_cast<const xmlpp::CommentNode*>(rootNode);

            Glib::ustring nodename = rootNode->get_name();
            if(nodename == "Logbook")   //root node corresponds to a SportTrack v3 Logbook
            {
                if(verboseImport) std::cout << "Found a SportTrack LogBook\n";

                xmlpp::Node::NodeList rootChildren = rootNode->get_children();
                for(xmlpp::Node::NodeList::iterator iterRootChildren = rootChildren.begin();
                    iterRootChildren != rootChildren.end();
                    ++iterRootChildren)
                {
                    xmlpp::Node* rootChildNode = dynamic_cast<const xmlpp::Node*>(*iterRootChildren);
                    if(rootChildNode->get_name() == "Activities")
                    {
                        if(verboseImport) std::cout << "Entering the list of activities\n";
                        xmlpp::Node::NodeList activitiesChildren = rootChildNode->get_children();
                        for(xmlpp::Node::NodeList::iterator iterActivitiesChildren = activitiesChildren.begin();
                            iterActivitiesChildren != activitiesChildren.end();
                            ++iterActivitiesChildren)
                        {
                            xmlpp::Node* activityNode = dynamic_cast<const xmlpp::Node*>(*iterActivitiesChildren);
                            if(activityNode->get_name() == "Activity")
                            {
                                if(verboseImport) std::cout << "Found an activity\n";
                                Activity newActivity;
                                const xmlpp::Element* activityNodeElement = dynamic_cast<const xmlpp::Element*>(activityNode);
                                const xmlpp::Element::AttributeList& activityNodeAttributes = activityNodeElement->get_attributes();
                                for(xmlpp::Element::AttributeList::const_iterator iterActivityNodeAttributes =
                                                                                                activityNodeAttributes.begin();
                                    iterActivityNodeAttributes != activityNodeAttributes.end();
                                    ++iterActivityNodeAttributes)
                                {
                                    const xmlpp::Attribute* activityAttribute = *iterActivityNodeAttributes;
                                    if(activityAttribute->get_name() == "startTime")
                                    {
                                        DateTime date;
                                        date.SetWithFormat(activityAttribute->get_value(), "yyyy-mm-ddThh:nn:ssZ");
                                        newActivity.SetDateTime(date);
                                    }
                                    if(activityAttribute->get_name() == "timeZoneUtcOffset")
                                    {
                                        std::istringstream stream(activityAttribute->get_value());
                                        int offset;
                                        stream >> offset;
                                        if(stream.fail())
                                            std::cerr << "Problem while reading activity attributes\n";
                                        ///@warning The date must be set before applying the offset
                                        DateTime date = newActivity.GetDateTime();
                                        ///@warning This may not work properly with activities happening around midnight
                                        date.SetHour(date.GetHour() + offset);
                                        newActivity.SetDateTime(date);
                                    }
                                    if(activityAttribute->get_name() == "totalCalories")
                                    {
                                        std::istringstream stream(activityAttribute->get_value());
                                        unsigned int calories;
                                        stream >> calories;
                                        if(stream.fail())
                                            std::cerr << "Problem while reading activity attributes\n";
                                        newActivity.SetCalories(calories);
                                    }
                                    if(activityAttribute->get_name() == "totalTime")
                                    {
                                        std::istringstream stream(activityAttribute->get_value());
                                        unsigned int seconds;
                                        stream >> seconds;
                                        if(stream.fail())
                                            std::cerr << "Problem while reading activity attributes\n";
                                        Duration duration;
                                        duration.SetSecond(seconds);
                                        duration.Normalize();
                                        newActivity.SetDuration(duration);
                                    }
                                    if(activityAttribute->get_name() == "totalDistance")
                                    {
                                        std::istringstream stream(activityAttribute->get_value());
                                        double distance;
                                        stream >> distance;
                                        if(stream.fail())
                                            std::cerr << "Problem while reading activity attributes\n";
                                        newActivity.SetDistance(distance / 1000.);
                                    }
                                    if(activityAttribute->get_name() == "averageHeartRate")
                                    {
                                        std::istringstream stream(activityAttribute->get_value());
                                        unsigned int fcMoy;
                                        stream >> fcMoy;
                                        if(stream.fail())
                                            std::cerr << "Problem while reading activity attributes\n";
                                        newActivity.SetFCMoy(fcMoy);
                                    }
                                    if(activityAttribute->get_name() == "location")
                                    {
                                        newActivity.SetLocation(activityAttribute->get_value());
                                    }
                                    if(activityAttribute->get_name() == "categoryName")
                                    {
                                        newActivity.SetActivityType(activityAttribute->get_value());
                                    }

                                    /*if(attribute->get_name() == "lat")
                                    {
                                        std::istringstream stream(attribute->get_value());
                                        stream >> lat;
                                        if(stream.fail() == true)
                                        {
                                            std::cerr << "Problem while reading trkpt attributes\n";
                                        }
                                    }*/
                                }
                                AddActivity(newActivity);
                            }   //Fin de la boucle qui gère le traitement d'une activité
                        }
                    }
                    else if(rootChildNode->get_name() == "Athlete")
                    {
                        std::cout << "Entering athlete description\n";
                        xmlpp::Node::NodeList athleteChildren = rootChildNode->get_children();
                        for(xmlpp::Node::NodeList::iterator iterAthleteChildren = athleteChildren.begin();
                            iterAthleteChildren != athleteChildren.end();
                            ++iterAthleteChildren)
                        {
                            xmlpp::Node* historyNode = dynamic_cast<const xmlpp::Node*>(*iterAthleteChildren);
                            if(historyNode->get_name() == "History")
                            {
                                if(verboseImport) std::cout << "Found an history\n";
                                xmlpp::Node::NodeList historyChildren = historyNode->get_children();
                                for(xmlpp::Node::NodeList::iterator iterHistoryChildren = historyChildren.begin();
                                    iterHistoryChildren != historyChildren.end();
                                    ++iterHistoryChildren)
                                {
                                    xmlpp::Node* entryNode = dynamic_cast<const xmlpp::Node*>(*iterHistoryChildren);
                                    if(entryNode->get_name() == "Entry")
                                    {
                                        if(verboseImport) std::cout << "Found an entry for the history\n";
                                        UserDay newUserDay;
                                        const xmlpp::Element* entryNodeElement =
                                                                dynamic_cast<const xmlpp::Element*>(entryNode);
                                        const xmlpp::Element::AttributeList& entryNodeAttributes =
                                                                entryNodeElement->get_attributes();
                                        for(xmlpp::Element::AttributeList::const_iterator iterEntryNodeAttributes =
                                                                            entryNodeAttributes.begin();
                                            iterEntryNodeAttributes != entryNodeAttributes.end();
                                            ++iterEntryNodeAttributes)
                                        {
                                            const xmlpp::Attribute* entryAttribute = *iterEntryNodeAttributes;
                                            if(entryAttribute->get_name() == "date")
                                            {
                                                DateTime date;
                                                date.SetWithFormat(entryAttribute->get_value(),"yyyy-mm-ddThh:nn:ssZ");
                                                newUserDay.SetDate(date);
                                            }
                                            if(entryAttribute->get_name() == "weight")
                                            {
                                                std::istringstream stream(entryAttribute->get_value());
                                                double weight;
                                                stream >> weight;
                                                if(stream.fail())
                                                    std::cerr << "Problem while reading history entry attributes\n";
                                                newUserDay.SetWeight(weight);
                                            }
                                            if(entryAttribute->get_name() == "bmi")
                                            {
                                                std::istringstream stream(entryAttribute->get_value());
                                                double bmi;
                                                stream >> bmi;
                                                if(stream.fail())
                                                    std::cerr << "Problem while reading history entry attributes\n";
                                                ///@todo The Body mass index property should be added to user day
                                                //newUserDay.SetBMI(bmi);
                                            }
                                        }
                                        AddUserDay(newUserDay);
                                    }   //End of userDay treatment
                                }
                            }   //End of history treatment
                        }
                    }
                }

                /*xmlpp::Node::NodeList list = pNode->get_children();
                for(xmlpp::Node::NodeList::iterator iter = list.begin();
                    iter != list.end();
                    ++iter)
                {
                    xmlpp::Node* currentNode = dynamic_cast<const xmlpp::Node*>(*iter);
                    if(currentNode->get_name() == "ActivityTypes")
                    ///@brief Entering list of activity types
                    {
                        activityTypes.ReadXMLNode(currentNode);
                    }
                    else if(currentNode->get_name() == "Activities")
                    ///@brief Entering list of activities
                    {
                        xmlpp::Node::NodeList listOfActivities
                            = currentNode->get_children();
                        for(xmlpp::Node::NodeList::iterator iterAct = listOfActivities.begin();
                            iterAct != listOfActivities.end();
                            ++iterAct)
                        {
                            xmlpp::Node* currentActNode =
                                dynamic_cast<const xmlpp::Node*>(*iterAct);
                            if(currentActNode->get_name() == "Activity")
                            {
                                Activity newActivity;
                                newActivity.ReadXMLNode(currentActNode);
                                activities.push_back(newActivity);
                            }
                        }
                    }
                    
                }*/
            }
        }
    }
    catch(const std::exception& ex)
    {
        std::cerr << "Exception caught: " << ex.what() << std::endl;
    }

    return true;
}

///@brief Read TrainingBook from a *.fitlog file
///@param filePath path to the *.fitlog file to read
bool TrainingBook::ReadFitlogFile(std::string filePath)
{
    try
    {
        xmlpp::DomParser parser;
        parser.set_substitute_entities();
        parser.parse_file(filePath);
        if(parser)
        {
            //Walk the tree
            const xmlpp::Node* rootNode = parser.get_document()->get_root_node();
            const xmlpp::ContentNode* nodeContent = dynamic_cast<const xmlpp::ContentNode*>(rootNode);
            const xmlpp::TextNode* nodeText = dynamic_cast<const xmlpp::TextNode*>(rootNode);
            const xmlpp::CommentNode* nodeComment = dynamic_cast<const xmlpp::CommentNode*>(rootNode);

            Glib::ustring nodeName = rootNode->get_name();
            if(nodeName == "FitnessWorkbook")
            {
                if(verboseImport) std::cout << "Found a fitlog workbook\n";
                unsigned int nb_activities_added = 0;
                xmlpp::Node::NodeList rootChildren = rootNode->get_children();
                for(xmlpp::Node::NodeList::iterator iterRootChildren = rootChildren.begin();
                    iterRootChildren != rootChildren.end();
                    ++iterRootChildren)
                {
                    xmlpp::Node* rootChildNode = dynamic_cast<xmlpp::Node*>(*iterRootChildren);
                    if(rootChildNode->get_name() == "AthleteLog")
                    {
                        xmlpp::Node::NodeList athleteLogChildren = rootChildNode->get_children();
                        for(xmlpp::Node::NodeList::iterator iterAthleteLogChildren = athleteLogChildren.begin();
                            iterAthleteLogChildren != athleteLogChildren.end();
                            ++iterAthleteLogChildren)
                        {
                            xmlpp::Node* activityNode = dynamic_cast<xmlpp::Node*>(*iterAthleteLogChildren);
                            if(activityNode->get_name() == "Activity")
                            {
                                if(verboseImport) std::cout << "Found an activity\n";
                                Activity newActivity;
                                bool distanceIsSet = false;
                                bool durationIsSet = false;
                                const xmlpp::Element* activityNodeElement = dynamic_cast<const xmlpp::Element*>(activityNode);
                                const xmlpp::Element::AttributeList& activityNodeAttributes = activityNodeElement->get_attributes();
                                for(xmlpp::Element::AttributeList::const_iterator iterActivityNodeAttributes =
                                                                                                    activityNodeAttributes.begin();
                                    iterActivityNodeAttributes != activityNodeAttributes.end();
                                    ++iterActivityNodeAttributes)
                                {
                                    const xmlpp::Attribute* activityAttribute = *iterActivityNodeAttributes;
                                    if(activityAttribute->get_name() == "StartTime")
                                    {
                                        DateTime date;
                                        date.SetWithFormat(activityAttribute->get_value(), "yyyy-mm-ddThh:nn:ssZ");
                                        newActivity.SetDateTime(date);
                                        ///@bug It seems that time is in UTC format. It should be corrected
                                    }
                                }
                                xmlpp::Node::NodeList activityChildren = activityNode->get_children();
                                for(xmlpp::Node::NodeList::iterator iterActivityChildren = activityChildren.begin();
                                    iterActivityChildren != activityChildren.end();
                                    ++iterActivityChildren)
                                {
                                    xmlpp::Node* activityChildNode = dynamic_cast<xmlpp::Node*>(*iterActivityChildren);
                                    if(activityChildNode->get_name() == "Duration")
                                    {
                                        durationIsSet = true;
                                        const xmlpp::Element* durationElement =
                                                                             dynamic_cast<const xmlpp::Element*>(activityChildNode);
                                        const xmlpp::Element::AttributeList& durationAttributes =
                                                                                                  durationElement->get_attributes();
                                        for(xmlpp::Element::AttributeList::const_iterator iterDurationAttributes =
                                                                                                         durationAttributes.begin();
                                            iterDurationAttributes != durationAttributes.end();
                                            ++iterDurationAttributes)
                                        {
                                            const xmlpp::Attribute* durationAttribute = *iterDurationAttributes;
                                            if(durationAttribute->get_name() == "TotalSeconds")
                                            {
                                                Duration duration;
                                                std::istringstream stream(durationAttribute->get_value());
                                                unsigned int seconds;
                                                stream >> seconds;
                                                if(stream.fail())
                                                    std::cerr << "Problem while reading duration attributes\n";
                                                duration.SetSecond(seconds);
                                                duration.Normalize();
                                                newActivity.SetDuration(duration);
                                            }
                                        }
                                    }   //End of duration reading
                                    else if(activityChildNode->get_name() == "Distance")
                                    {
                                        distanceIsSet = true;
                                        const xmlpp::Element* distanceElement =
                                                                           dynamic_cast<const xmlpp::Element*>(activityChildNode);
                                        const xmlpp::Element::AttributeList& distanceAttributes = distanceElement->get_attributes();
                                        for(xmlpp::Element::AttributeList::const_iterator iterDistanceAttributes =
                                                                                                distanceAttributes.begin();
                                            iterDistanceAttributes != distanceAttributes.end();
                                            ++iterDistanceAttributes)
                                        {
                                            const xmlpp::Attribute* distanceAttribute = *iterDistanceAttributes;
                                            if(distanceAttribute->get_name() == "TotalMeters")
                                            {
                                                double distance;
                                                std::istringstream stream(distanceAttribute->get_value());
                                                stream >> distance;
                                                if(stream.fail())
                                                    std::cerr << "Problem while reading distance attributes\n";
                                                newActivity.SetDistance(distance/1000.);
                                            }
                                        }
                                    }   //End of distance reading
                                    else if(activityChildNode->get_name() == "Calories")
                                    {
                                        const xmlpp::Element* caloriesElement =
                                                                          dynamic_cast<const xmlpp::Element*>(activityChildNode);
                                        const xmlpp::Element::AttributeList& caloriesAttributes = caloriesElement->get_attributes();
                                        for(xmlpp::Element::AttributeList::const_iterator iterCaloriesAttributes =
                                                                                                     caloriesAttributes.begin();
                                            iterCaloriesAttributes != caloriesAttributes.end();
                                            ++iterCaloriesAttributes)
                                        {
                                            const xmlpp::Attribute* caloriesAttribute = *iterCaloriesAttributes;
                                            if(caloriesAttribute->get_name() == "TotalCal")
                                            {
                                                int calories;
                                                std::istringstream stream(caloriesAttribute->get_value());
                                                stream >> calories;
                                                if(stream.fail())
                                                    std::cerr << "Problem while reading calories attributes\n";
                                                newActivity.SetCalories(calories);
                                            }
                                        }
                                    }   //End of calories reading
                                    else if(activityChildNode->get_name() == "Category")
                                    {
                                        const xmlpp::Element* categoryElement =
                                                                            dynamic_cast<const xmlpp::Element*>(activityChildNode);
                                        const xmlpp::Element::AttributeList& categoryAttributes = categoryElement->get_attributes();
                                        for(xmlpp::Element::AttributeList::const_iterator iterCategoryAttributes =
                                                                                                    categoryAttributes.begin();
                                            iterCategoryAttributes != categoryAttributes.end();
                                            ++iterCategoryAttributes)
                                        {
                                            const xmlpp::Attribute* categoryAttribute = *iterCategoryAttributes;
                                            if(categoryAttribute->get_name() == "Name")
                                            {
                                                std::string categoryName = categoryAttribute->get_value();
                                                newActivity.SetActivityType(categoryName);
                                                if(!activityTypes.Exists(categoryName));
                                                    activityTypes.Add(categoryName);
                                            }
                                        }
                                    }   //End of category reading
                                    else if(activityChildNode->get_name() == "Location")
                                    {
                                        const xmlpp::Element* locationElement =
                                                                            dynamic_cast<const xmlpp::Element*>(activityChildNode);
                                        const xmlpp::Element::AttributeList& locationAttributes = locationElement->get_attributes();
                                        for(xmlpp::Element::AttributeList::const_iterator iterLocationAttributes =
                                                                                                    locationAttributes.begin();
                                            iterLocationAttributes != locationAttributes.end();
                                            ++iterLocationAttributes)
                                        {
                                            const xmlpp::Attribute* locationAttribute = *iterLocationAttributes;
                                            if(locationAttribute->get_name() == "Name")
                                            {
                                                newActivity.SetLocation(locationAttribute->get_value());
                                            }
                                        }
                                    }   //End of location reading
                                    else if(activityChildNode->get_name() == "Name")
                                    {
                                        ///@todo Managing activity name
                                    }
                                    else if(activityChildNode->get_name() == "EquipmentUsed")
                                    {
                                        //Plus compliqué : il y a des noeuds fils
                                        ///@todo Managing a list of equipments
                                    }
                                    else if(activityChildNode->get_name() == "Track")
                                    {
                                        if(verboseImport) std::cout << "Found a GPS track\n";
                                        const xmlpp::Element* trackElement =
                                                                        dynamic_cast<const xmlpp::Element*>(activityChildNode);
                                        const xmlpp::Element::AttributeList& trackAttributes = trackElement->get_attributes();
                                        DateTime trackStartTime;
                                        for(xmlpp::Element::AttributeList::const_iterator iterTrackAttributes =
                                                                                                    trackAttributes.begin();
                                            iterTrackAttributes != trackAttributes.end();
                                            ++iterTrackAttributes)
                                        {
                                            const xmlpp::Attribute* trackAttribute = *iterTrackAttributes;
                                            if(trackAttribute->get_name() == "StartTime")
                                            {
                                                trackStartTime.SetWithFormat(trackAttribute->get_value(), "yyyy-mm-ddThh:nn:ssZ");
                                                ///@bug It seems that time is in UTC format. It should be corrected
                                            }
                                        }
                                        TrackSegment segment;
                                        xmlpp::Node::NodeList trackPointNodes = activityChildNode->get_children();
                                        for(xmlpp::Node::NodeList::iterator iterTrackPoints = trackPointNodes.begin();
                                            iterTrackPoints != trackPointNodes.end();
                                            ++iterTrackPoints)
                                        {
                                            xmlpp::Node* trackPointNode = dynamic_cast<xmlpp::Node*>(*iterTrackPoints);
                                            if(trackPointNode->get_name() == "pt")
                                            {
                                                TrackPoint point;
                                                const xmlpp::Element* trackPointElement =
                                                                               dynamic_cast<const xmlpp::Element*>(trackPointNode);
                                                const xmlpp::Element::AttributeList& trackPointAttributes =
                                                                                                trackPointElement->get_attributes();
                                                for(xmlpp::Element::AttributeList::const_iterator iterTrackPointAttributes =
                                                                                                    trackPointAttributes.begin();
                                                    iterTrackPointAttributes != trackPointAttributes.end();
                                                    ++iterTrackPointAttributes)
                                                {
                                                    const xmlpp::Attribute* trackPointAttribute = *iterTrackPointAttributes;
                                                    if(trackPointAttribute->get_name() == "tm")
                                                    {
                                                        unsigned int timeInc;
                                                        std::istringstream stream(trackPointAttribute->get_value());
                                                        stream >> timeInc;
                                                        if(stream.fail())
                                                            std::cerr << "Problem while reading track point attributes\n";
                                                        Duration ptDuration;
                                                        ptDuration.SetSecond(timeInc);
                                                        ptDuration.Normalize();
                                                        DateTime ptTime = trackStartTime + ptDuration;
                                                        point.SetTime(ptTime);
                                                    }
                                                    else if(trackPointAttribute->get_name() == "lat")
                                                    {
                                                        double lat;
                                                        std::istringstream stream(trackPointAttribute->get_value());
                                                        stream >> lat;
                                                        if(stream.fail())
                                                            std::cerr << "Problem while reading track point attributes\n";
                                                        point.SetLat(lat);
                                                    }
                                                    else if(trackPointAttribute->get_name() == "lon")
                                                    {
                                                        double lon;
                                                        std::istringstream stream(trackPointAttribute->get_value());
                                                        stream >> lon;
                                                        if(stream.fail())
                                                            std::cerr << "Problem while reading track point attribute\n";
                                                        point.SetLon(lon);
                                                    }
                                                    else if(trackPointAttribute->get_name() == "ele")
                                                    {
                                                        double ele;
                                                        std::istringstream stream(trackPointAttribute->get_value());
                                                        stream >> ele;
                                                        if(stream.fail())
                                                            std::cerr << "Problem while reading track point attributes\n";
                                                        point.SetElevation(ele);
                                                    }
                                                }
                                                segment.AddPoint(point);   
                                            }
                                        }
                                        Track track;
                                        track.AddSegment(segment);
                                        ///@warning Reading of track should be removed
                                        //newActivity.SetTrack(track);
                                        //newActivity.SetTrackIsEmbedded(true);
                                    }   //End of track reading
                                    else if(activityChildNode->get_name() == "Notes")
                                    {
                                        std::string commentsString =
                                                ((xmlpp::TextNode*)(*(activityChildNode->get_children()).begin()))->get_content();
                                        newActivity.SetComments(commentsString);
                                    }   //End of notes reading
                                    else if(activityChildNode->get_name() == "Weather")
                                    {
                                        //Différentes valeurs possibles
                                        //       Condition = Clouds
                                        //       ....
                                        //       Temp = "13"
                                        ///@todo Managing weather
                                    }
                                    else if(activityChildNode->get_name() == "HeartRate")
                                    {
                                        const xmlpp::Element* heartrateElement =
                                                                dynamic_cast<const xmlpp::Element*>(activityChildNode);
                                        const xmlpp::Element::AttributeList& heartrateAttributes =
                                                                                    heartrateElement->get_attributes();
                                        for(xmlpp::Element::AttributeList::const_iterator iterHeartrateAttributes =
                                                                                                    heartrateAttributes.begin();
                                            iterHeartrateAttributes != heartrateAttributes.end();
                                            ++iterHeartrateAttributes)
                                        {
                                            const xmlpp::Attribute* heartrateAttribute = *iterHeartrateAttributes;
                                            if(heartrateAttribute->get_name() == "AverageBPM")
                                            {
                                                unsigned int fcMoy;
                                                std::istringstream stream(heartrateAttribute->get_value());
                                                stream >> fcMoy;
                                                if(stream.fail())
                                                    std::cerr << "Problem while reading heartrate attributes\n";
                                                newActivity.SetFCMoy(fcMoy);
                                            }
                                        }
                                    }
                                }
                                if(!distanceIsSet)
                                    newActivity.SetDistanceFromTrack();
                                if(!durationIsSet)
                                    newActivity.SetDurationFromTrack();
                                AddActivity(newActivity);
                                nb_activities_added++;
                                if(verboseImport) std::cout << nb_activities_added << " activities added\n";
                            }   //End of activity reading
                        }
                    }
                }
            }
        }
    }
    catch(const std::exception& ex)
    {
        std::cerr << "Exception caught: " << ex.what() << std::endl;
    }

    std::cout << "Importation finished\n";
    return true;
}

///@brief Sort the activities in ascending order depending on their date and time
void TrainingBook::SortActivities()
{
    ///@todo This function should be tested
    activities.sort();
}
